using System;
using System.Collections.Generic;

namespace IQIGame.Onigao.Framework
{
    public interface IReusableClass
    {
        //最大保留数量
        uint MaxStore { get; }
        //重置
        void OnReset();

    }

    public interface IReusableClassWithFree : IReusableClass
    {
        void Free();
    }

    /// <summary>
    /// 对象池
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ClassPool<T> : ClassPool where T : class, IReusableClass, new()
    {
        /// <summary>
        /// Pool
        /// </summary>
        private readonly Stack<T> _pool = new();
        /// <summary>
        /// 唯一单例, 不能改名字, ClassPool.PutAutoType反射会用
        /// </summary>
        private readonly static ClassPool<T> Instance = new();
        private ClassPool() { }

        public static T Get()
        {
            return Instance._Get();
        }

        public static void Put(T data)
        {
            Instance._Put(data);
        }

        public static void Clear()
        {
            Instance.ClearPool();
        }

        public static int PoolCount => Instance._pool.Count;

        private T _Get()
        {
            _pool.TryPop(out T result);
            if (result == null)
            {
                return new T();
            }
#if UNITY_EDITOR
            //取出的时候 检查对象是否完全重置
            //如果对象被捕获了, 回收后还会被改变, 取出的时候检查是有必要的
            ClassPoolCheckUtil.CheckPoolClassReset(typeof(T), result);
#endif
            //UnityEngine.Debug.LogWarning($"{typeof(ClassPool<T>)} is Get Success");
            return result;
        }

        private void _Put(T data)
        {
            if (data == null)
            {
                LogGame.LogError($"{typeof(ClassPool<T>)} Put data is null");
                return;
            }

#if UNITY_EDITOR
            if (data.GetType() != typeof(T))
            {
                UnityEngine.Debug.LogError($"{typeof(ClassPool<T>)} Put Type Error, Current Type:" + typeof(T).Name + " Real Type:" + data.GetType().Name);
                return;
            }
#endif

            bool canPutIntoPool = _pool.Count < data.MaxStore * Factor;
#if UNITY_EDITOR
            if (!canPutIntoPool)
            {
                UnityEngine.Debug.LogWarning($"{typeof(ClassPool<T>)}, MaxStore：{data.MaxStore * Factor}");
            }
#endif
            data.OnReset();
#if UNITY_EDITOR
            ClassPoolCheckUtil.CheckPoolClassReset(typeof(T), data);
            foreach (var item in _pool)
            {
                if (ReferenceEquals(item, data))//需要用ReferenceEquals,防止重写==或equals
                {
                    UnityEngine.Debug.LogError($"{typeof(ClassPool<T>)} Put Error");
                    return;
                }
            }
#endif
            if (canPutIntoPool)
            {
                _pool.Push(data);
            }
            else if (data is IDisposable disposable)
            {
                disposable.Dispose();
            }
        }

        protected override void ClearPool()
        {
            _pool.Clear();
        }

        protected override void PutData(IReusableClass data)
        {
            _Put(data as T);
        }

        protected override Type PoolType => typeof(T);
    }

    public abstract class ClassPool
    {
        /// <summary>
        /// 容量系数, 放到不同的环境下系数可能会有变化
        /// </summary>
        public static double Factor = 1;
        /// <summary>
        /// 所有的ClassPool
        /// </summary>
        private readonly static Dictionary<Type, ClassPool> _allPools = new();

        protected ClassPool()
        {
            _allPools.Add(this.PoolType, this);
        }

        public static T Get<T>() where T : class, IReusableClass, new()
        {
            return ClassPool<T>.Get();
        }

        public static void Put<T>(T data) where T : class, IReusableClass, new()
        {
            ClassPool<T>.Put(data);
        }

        public static void PutAutoType(IReusableClass data)
        {
            if (data == null)
            {
                LogGame.LogError($"{typeof(ClassPool)} PutAutoType data is null");
                return;
            }
            Type type = data.GetType();
            if (_allPools.TryGetValue(type, out ClassPool pool))
            {
                pool.PutData(data);
            }
            else
            {
                //如果没有从池里面拿 就put回来会走到这里
                Type classPoolType = typeof(ClassPool<>).MakeGenericType(type);
                System.Reflection.FieldInfo fieldInfo = classPoolType.GetField("Instance", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
                ClassPool newPool = (ClassPool)fieldInfo.GetValue(null);
                newPool.PutData(data);

#if UNITY_EDITOR
                LogGame.LogError("对象换回Pool时，没有找到对应的Pool，请检查是否对象并没有通过ClassPool.Get创建，而是直接new。");
#endif
            }
        }

        public static void ClearAll()
        {
            foreach (var pool in _allPools.Values)
            {
                pool.ClearPool();
            }
        }

        protected abstract void ClearPool();
        protected abstract void PutData(IReusableClass data);
        protected abstract Type PoolType { get; }

    }

}
